package com.boboPlanet.comm.log;

import com.alibaba.fastjson.JSON;
import com.boboPlanet.comm.enums.GatewayConstance;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @Description: TODO 参数是个对象，未解决
 * @Author: mis_wu
 * @Date: 2023/6/30
 * @Company planet
 **/
@Aspect
@Component
@Slf4j
public class RequestLoggingAspect {

    private static final ThreadLocal<Long> threadLocal = new NamedThreadLocal<>("request-time");

    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void logCollectAnnotationPointCutGet(){}

    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public void logCollectAnnotationPointCutPost(){}

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void logCollectAnnotationPointCutRM(){}

    @Around("logCollectAnnotationPointCutGet() || logCollectAnnotationPointCutPost() || logCollectAnnotationPointCutRM()")
    public Object logRequest(@NotNull ProceedingJoinPoint joinPoint) throws Throwable {
        threadLocal.set(System.currentTimeMillis());
        // 获取请求的相关信息
        ServletRequestAttributes attributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteAddr();
        String url = request.getRequestURL().toString();
        //String method = joinPoint.getSignature().toLongString();
        String requestMethod = request.getMethod();
        Object[] args = joinPoint.getArgs();
        //args = Arrays.stream(args).filter(p -> !(p instanceof HttpServletResponse || p instanceof HttpServletRequest)).toArray();
        String prams = "";
        if (args.length > 0){
            if (GatewayConstance.POST_REQUEST.equals(requestMethod)){
                Object arg = args[0];
                if (arg != null){
                    Map<String, Object> param = getParam(arg);
                    try {
                        prams = JSON.toJSONString(param);
                    } catch (Exception e) {
                        //解决aop坏绕通知getOutputStream() has already been called问题
                        prams = param.toString();
                        log.error("request logging aspect exception,reason is: {}",e.getMessage(),e);
                    }
                }
            } else if (GatewayConstance.GET_REQUEST.equals(requestMethod)) {
                prams = request.getQueryString();
            }
        }
        // 执行目标方法
        Object result = joinPoint.proceed();
        // 记录日志
        LogContent content = new LogContent(ip,url, requestMethod, prams,result);
        log.info("请求ip地址:{}",content.getIp());
        log.info("请求地址:{}",content.getUrl());
        log.info("请求方法:{}",content.getMethod());
        log.info("请求参数:{}",content.getArgs());
        log.info("请求返回值:{}",content.getResult());
        Long endTime = System.currentTimeMillis();
        log.info("请求耗时:{} ms", endTime - threadLocal.get());
        return result;
    }

    /**
     * 请求参数处理
     * @param obj POST类对象
     * @return MAP
     */
    public static Map<String,Object> getParam(Object obj){
        Map<String, Object> map = new HashMap<>();
        Class<?> aClass = obj.getClass();
        //属性集合
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            //设置属性可访问
            field.setAccessible(true);
            Object val;
            try {
                val = field.get(obj);
                //获取值并设置
                map.put(field.getName(),val);
            } catch (IllegalAccessException e) {
                log.error("解析请求参数异常，原因：{}",e.getMessage(),e);
            }
        }
        return map;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class LogContent{
        private String ip;
        private String url;
        private String method;
        private String args;
        private Object result;
    }

}
